//
// Created by jiaxv on 2025/8/8.
//

#include "as_entity.h"

#include "define.h"
#include "util/log.h"
#include "util/util.h"


size_t tag_define_sz = TAG_COUNT;
const tag_defines_t tag_defines[] = {
    {6, "Serial", 6},
    {10, "AS UA", 5},
    {12, "AS SAC", 6},
    { 12, "GS SAC",  6},
    {12, "NEXT_PEER", 9},
    {46, "AS_IP", 5},
    {46, "GS_IP", 5},
    {9, "GS_PORT", 7},
    {20, "STATUS", 6}
};


static json_tmpl_t tag_tmpl[] = {
    {cJSON_Number, sizeof(uint32_t), "UA", "UA", NULL},
    {cJSON_Number, sizeof(uint16_t), "SAC", "SAC", NULL},
    {cJSON_Number, sizeof(uint16_t), "current_GS", "current_GS", NULL},
    {cJSON_Number, sizeof(uint16_t), "next_GS", "next_GS", NULL},
    {cJSON_String, sizeof(buffer_t *), "AS_IP", "AS_IP", NULL},
    {cJSON_String, sizeof(buffer_t *), "GS_IP", "GS_IP", NULL},
    {cJSON_Number, sizeof(uint16_t), "port", "port", NULL},
    {cJSON_String, sizeof(buffer_t *), "status","status", NULL},
    {cJSON_Invalid, 0, NULL, NULL, NULL}
};

json_tmpl_desc_t tag_tmpl_desc = {
    .desc = "TAG",
    .tmpl = tag_tmpl,
    .size = sizeof(tag_json_t)
};

as_entity_t *init_as_entity(uint32_t UA, uint16_t SAC, uint16_t current_peer_sac, element_type_t ele_type, net_element_t *net_ele) {

    as_entity_t *en = calloc(1, sizeof(as_entity_t));
    en->UA = UA;
    en->SAC = SAC;
    en->current_client_peer_sac = current_peer_sac;

    switch (ele_type) {
        case ELEMENT_AMM:
            cn_log_info(net_ele->element_tag, INTERFACE_UNDEFINED," AS { UA: %d } has registered in", UA);
            init_amm_fsm(&en->amm_sm);
            break;
        case ELEMENT_DBSM:
            cn_log_info(net_ele->element_tag, INTERFACE_UNDEFINED," AS { SAC: %d } has registered in", SAC);
            init_dbsm_fsm(&en->dbsm_sm);
            break;
        case ELEMENT_GSG:
            cn_log_info(net_ele->element_tag, INTERFACE_UNDEFINED," AS { UA: %d } has registered in", UA);
            init_gsg_fsm(&en->gsg_sm);
            break;
        case ELEMENT_RDF:
            cn_log_info(net_ele->element_tag, INTERFACE_UNDEFINED," AS { SAC:%d } has registered in", SAC);
            break;
        default: {
            free(en);
            return NULL;
        }
    }

    return en;
}

as_entity_t *init_as_entity_ip(uint16_t SAC, char *IP_AS, char *IP_GS, uint16_t port_GS) {

    as_entity_t *en = calloc(1, sizeof(as_entity_t));
    en->SAC = SAC;
    en->port_GS = port_GS;
    memcpy(en->IP_AS, IP_AS, GEN_ADDRLEN);
    memcpy(en->IP_GS, IP_GS, GEN_ADDRLEN);

    return en;
}

static void print_as_info_segment(uint8_t *widths) {
    for (int r = 0; r < TAG_COUNT; r++) {
        if (!widths[r]) continue;
        printf("+");
        for (int i = 0; i < widths[r]; i++) {
            printf("-");
        }
    }

    printf("+\n");
}

static void print_as_info_header(const char *tag, uint8_t *widths) {

    int total_width = 0;
    for (int i = 0; i < tag_define_sz; i++) {
        if (widths[i] != 0) {
            total_width += widths[i];
            total_width ++; // for "|"
        }
    }

    int first_width = total_width - 1; // 最后一个|是冗余的
    printf("+");
    while (first_width--) {
        printf("-");
    }
    printf("+\n");

    printf("| %s STATUS TABLE %*s |\n", tag, total_width - (int)strlen(tag) - 16 - 1, "");

    print_as_info_segment(widths);

    for (int r = 0; r < TAG_COUNT; r++) {
        if (!widths[r]) continue;
        printf("|");
        printf("%s%*s", tag_defines[r].tag_name, widths[r] - tag_defines[r].tag_len, "");
    }
    printf("|\n");

    print_as_info_segment(widths);
}

static void print_as_info_detail(size_t serial, element_type_t type, as_entity_t *as, uint8_t *widths) {
    for (int r = 0; r < TAG_COUNT; r++) {
        if (!widths[r]) continue;
        char tag_str[128] = {0};
        printf("|");
        switch (r) {
            case TAG_SERIAL: {
                sprintf(tag_str, "%lu", serial);
                break;
            }
            case TAG_UA: {
                sprintf(tag_str, "%u", as->UA);
                break;
            }
            case TAG_SAC: {
                sprintf(tag_str, "%u", as->SAC);
                break;
            }
            case TAG_CURRENT_PEER: {
                // sprintf(tag_str, "%u", as->current_client_peer_sac);
                if (as->current_client_peer_sac == 16) {
                    sprintf(tag_str, "%u (GS1)", as->current_client_peer_sac);
                }else {
                    sprintf(tag_str, "%u (GS2)", as->current_client_peer_sac);
                }
                break;
            }
            case TAG_NEXT_PEER: {
                sprintf(tag_str, "%u", as->next_peer_sac);
                break;
            }
            case TAG_GS_PORT: {
                sprintf(tag_str, "%u", as->port_GS);
                break;
            }
            case TAG_AS_IP: {

                if (is_memory_zero(as->IP_AS, GEN_ADDRLEN)) {
                    tag_str[0] = '-';
                }else {
                    struct in6_addr addr;
                    // 将字节数组复制到in6_addr结构体中
                    memcpy(addr.s6_addr, as->IP_AS, 16);

                    // 使用inet_ntop将其转换为可读的字符串格式
                    if (inet_ntop(AF_INET6, &addr, tag_str, INET6_ADDRSTRLEN) == NULL) {
                        strcpy(tag_str, "Invalid IPv6 Address");
                    }
                }
                break;
            }
            case TAG_GS_IP: {
                if (is_memory_zero(as->IP_GS, GEN_ADDRLEN)) {
                    tag_str[0] = '-';
                }else {
                    struct in6_addr addr;
                    memset(&addr, 0, sizeof(addr));
                    // 将字节数组复制到in6_addr结构体中
                    if (as->port_GS == 50010) {
                        addr.s6_addr[0] = 0x20;
                        addr.s6_addr[1] = 0x01;
                        addr.s6_addr[6] = 0xE3;
                        addr.s6_addr[7] = 0x04;
                        addr.s6_addr[14] = 0x01;
                        addr.s6_addr[15] = 0x11;
                    }else {
                        addr.s6_addr[0] = 0x20;
                        addr.s6_addr[1] = 0x01;
                        addr.s6_addr[6] = 0xE3;
                        addr.s6_addr[7] = 0x04;
                        addr.s6_addr[8] = 0x04;
                        addr.s6_addr[14] = 0x01;
                        addr.s6_addr[15] = 0x21;
                    }

                    char ip[64];
                    // 使用inet_ntop将其转换为可读的字符串格式
                    if (inet_ntop(AF_INET6, &addr, ip, INET6_ADDRSTRLEN) == NULL) {
                        strcpy(tag_str, "Invalid IPv6 Address");
                    }

                    if (as->port_GS == 50010) {
                        sprintf(tag_str, "%s (GS1)", ip);
                    }else {
                        sprintf(tag_str, "%s (GS2)", ip);
                    }
                }
                break;
            }
            case TAG_STATUS: {
                switch (type) {
                    case ELEMENT_AMM: {
                        char *str = stateM_currentState(&as->amm_sm)->data;
                        memcpy(tag_str, str, strlen(str));
                        break;
                    }
                    case ELEMENT_GSG: {
                        char *str = stateM_currentState(&as->gsg_sm)->data;
                        memcpy(tag_str, str, strlen(str));
                        break;
                    }
                    case ELEMENT_DBSM: {
                        char *str = stateM_currentState(&as->dbsm_sm)->data;
                        memcpy(tag_str, str, strlen(str));
                        break;
                    }
                    default: {
                        break;
                    }
                }
                break;
            }
            default:
                break;
        }
        printf("%s%*s", tag_str, (int)(widths[r] - strlen(tag_str)), "");
    }
    printf("|\n");
}

static l_err generate_as_info_json_single(element_type_t type, as_entity_t *as, struct dashboard_set_cn_data_s *set) {
    if (!as) {
        log_warn("AS struct is NULL");
        return LD_ERR_NULL;
    }

    tag_json_t tag = {0};

    tag.UA = as->UA;
    tag.SAC = as->SAC;
    tag.CURR_GS_SAC = as->current_client_peer_sac;
    tag.NEXT_GS_SAC = as->next_peer_sac;
    tag.GS_port = as->port_GS;

    /* AS IP */
    tag.AS_IP = init_buffer_ptr(128);
    if (is_memory_zero(as->IP_AS, GEN_ADDRLEN)) {
        *tag.AS_IP->ptr = '-';
    }else {
        struct in6_addr addr = {0};
        // 将字节数组复制到in6_addr结构体中
        memcpy(addr.s6_addr, as->IP_AS, 16);
        // 使用inet_ntop将其转换为可读的字符串格式
        if (inet_ntop(AF_INET6, &addr, (char *)tag.AS_IP->ptr, INET6_ADDRSTRLEN) == NULL) {
            strcpy((char *)tag.AS_IP->ptr, "Invalid IPv6 Address");
        }
    }

    /* GS IP */
    tag.GS_IP = init_buffer_ptr(128);
    if (is_memory_zero(as->IP_GS, GEN_ADDRLEN)) {
        *tag.GS_IP->ptr = '-';
    }else {
        struct in6_addr addr = {0};
        // 将字节数组复制到in6_addr结构体中
        if (as->port_GS == 50010) {
            addr.s6_addr[0] = 0x20;
            addr.s6_addr[1] = 0x01;
            addr.s6_addr[6] = 0xE3;
            addr.s6_addr[7] = 0x04;
            addr.s6_addr[14] = 0x01;
            addr.s6_addr[15] = 0x11;
        }else {
            addr.s6_addr[0] = 0x20;
            addr.s6_addr[1] = 0x01;
            addr.s6_addr[6] = 0xE3;
            addr.s6_addr[7] = 0x04;
            addr.s6_addr[8] = 0x04;
            addr.s6_addr[14] = 0x01;
            addr.s6_addr[15] = 0x21;
        }

        char ip[64];
        // 使用inet_ntop将其转换为可读的字符串格式
        if (inet_ntop(AF_INET6, &addr, ip, INET6_ADDRSTRLEN) == NULL) {
            strcpy((char *)tag.GS_IP->ptr, "Invalid IPv6 Address");
        }

        if (as->port_GS == 50010) {
            sprintf((char *)tag.GS_IP->ptr, "%s (GS1)", ip);
        }else {
            sprintf((char *)tag.GS_IP->ptr, "%s (GS2)", ip);
        }
    }

    /* status */
    tag.status = init_buffer_unptr();
    char *str = NULL;
    switch (type) {
        case ELEMENT_AMM: {
            str = stateM_currentState(&as->amm_sm)->data;
            break;
        }
        case ELEMENT_GSG: {
            str = stateM_currentState(&as->gsg_sm)->data;
            break;
        }
        case ELEMENT_DBSM: {
            str = stateM_currentState(&as->dbsm_sm)->data;
            break;
        }
        default: {
            str = "-";
            break;
        }
    }
    CLONE_TO_CHUNK(*(tag.status), (uint8_t *)str, strlen(str));

    // char *tag_str = NULL;
    // get_json_str(&tag, &tag_tmpl_desc, &tag_str);
    // log_warn("%s", tag_str);
    // free(tag_str);

    set->infos[set->info_size++] = marshel_json(&tag, &tag_tmpl_desc);

    free_buffer(tag.AS_IP);
    free_buffer(tag.GS_IP);
    free_buffer(tag.status);

    return LD_OK;
}

l_err generate_as_info_json(net_element_t *net_ele, dashboard_set_cn_data_t *set) {
    size_t iter = 0;
    void *item;
    while (hashmap_iter(net_ele->as_map, &iter, &item)) {
        as_entity_t *as = item;
        switch (net_ele->element_type) {
            case ELEMENT_DBSM:
            case ELEMENT_RDF: {
                if (is_memory_zero(as->IP_GS, GEN_ADDRLEN)) {
                    continue;
                }
                break;
            }
            case ELEMENT_GSG: {
                if (is_memory_zero(as->IP_AS, GEN_ADDRLEN)) {
                    continue;
                }
                break;
            }
            case ELEMENT_AMM: {
                char *str = stateM_currentState(&as->amm_sm)->data;
                if (strlen(str) != 13) {
                    continue;
                }
                break;
            }
        }
        generate_as_info_json_single(net_ele->element_type, as, set);

    }
    return LD_OK;
}


l_err logoff_as_entity(as_entity_t *as) {
    return LD_OK;
}

l_err set_as_gs_ip(as_entity_t *as, char *new_IP, uint16_t new_port) {
    zero(as->IP_GS);
    memcpy(as->IP_GS, new_IP, GEN_ADDRLEN);
    as->port_GS = new_port;

    return LD_OK;
}

l_err set_as_as_ip(as_entity_t *as, char *new_IP) {
    zero(as->IP_AS);
    memcpy(as->IP_AS, new_IP, GEN_ADDRLEN);

    return LD_OK;
}

/**
 * 以UA为索引
 */
uint64_t hash_as_uenode(const void *item, uint64_t seed0, uint64_t seed1) {
    const as_entity_t *as = item;
    return hashmap_sip(&as->UA, sizeof(uint32_t), seed0, seed1);
}

/**
 * 以SAC为索引 (DBSM)
 */
uint64_t hash_as_senode(const void *item, uint64_t seed0, uint64_t seed1) {
    const as_entity_t *as = item;
    return hashmap_sip(&as->SAC, sizeof(uint16_t), seed0, seed1);
}

struct hashmap *init_as_uenode_map() {
    return hashmap_new(sizeof(as_entity_t), 0, 0, 0,
                       hash_as_uenode, NULL, NULL, NULL);
}

struct hashmap *init_as_senode_map() {
    return hashmap_new(sizeof(as_entity_t), 0, 0, 0,
                       hash_as_senode, NULL, NULL, NULL);
}

as_entity_t *set_as_enode(struct hashmap *map, as_entity_t *en) {
    if (!en) return NULL;

    const void *ret = hashmap_set(map, en);
    /* !!!Do not free the previous entity !!! */
    // free(en);
    return (as_entity_t *)ret;
}
bool has_as_uenode(struct hashmap *map, const uint32_t UA) {
    return hashmap_get(map, &(as_entity_t){
                           .UA = UA
                       }) != NULL;
}

bool has_as_senode(struct hashmap *map, const uint16_t SAC) {
    return hashmap_get(map, &(as_entity_t){
                           .SAC = SAC
                       }) != NULL;
}

as_entity_t *get_as_uenode_by_UA(struct hashmap *map, uint32_t UA) {
    return hashmap_get(map, &(as_entity_t){
                           .UA = UA,
                       });
}

as_entity_t *get_as_uenode_by_SAC(struct hashmap *map, uint16_t SAC) {
    size_t iter = 0;
    void *item;
    while (hashmap_iter(map, &iter, &item)) {
        as_entity_t *node = item;
        if (node->SAC == SAC)
            return node;
    }
    return NULL;
}

as_entity_t *print_as_info(net_element_t *net_ele, struct hashmap *map, uint16_t mask) {
    size_t iter = 0;
    void *item;

    uint16_t bit_mask = 1;
    uint8_t widths[TAG_COUNT] = {0};
    for (int i = 0; i < TAG_COUNT; i++) {
        if (mask & bit_mask) {
            widths[i] = tag_defines[i].width;
        }
        bit_mask <<= 1;
    }
    print_as_info_header(net_ele->element_tag, widths);
    size_t as_serial = 0;
    while (hashmap_iter(map, &iter, &item)) {
        as_entity_t *as = item;

        // pre-ckeck
        switch (net_ele->element_type) {
            case ELEMENT_DBSM:
            case ELEMENT_RDF: {
                if (is_memory_zero(as->IP_GS, GEN_ADDRLEN)) {
                    continue;
                }
                break;
            }
            case ELEMENT_GSG: {
                if (is_memory_zero(as->IP_AS, GEN_ADDRLEN)) {
                    continue;
                }
                break;
            }
            case ELEMENT_AMM: {
                char *str = stateM_currentState(&as->amm_sm)->data;
                if (strlen(str) != 13) {
                    continue;
                }
                break;
            }
        }
        print_as_info_detail(++as_serial, net_ele->element_type, as, widths);
    }
    print_as_info_segment(widths);
    return NULL;
}

as_entity_t *get_as_senode_by_SAC(struct hashmap *map, uint16_t SAC) {
    return hashmap_get(map, &(as_entity_t){
                           .SAC = SAC,
                       });
}

as_entity_t *get_as_senode_by_as_IP(struct hashmap *map, char *IP) {
    size_t iter = 0;
    void *item;
    while (hashmap_iter(map, &iter, &item)) {
        as_entity_t *node = item;
        if (memcmp(node->IP_AS, IP, GEN_ADDRLEN) == 0)
            return node;
    }
    return NULL;
}


l_err delete_element_uenode_by_SAC(struct hashmap *map, uint16_t SAC) {
    as_entity_t *en = get_as_uenode_by_SAC(map, SAC);
    if (en) {
        logoff_as_entity(en);
        hashmap_delete(map, en);
        return LD_OK;
    }
    return LD_ERR_INTERNAL;
}

l_err delete_element_senode_by_SAC(struct hashmap *map, uint16_t SAC) {
    as_entity_t *en = get_as_senode_by_SAC(map, SAC);
    if (en) {
        logoff_as_entity(en);
        hashmap_delete(map, en);
        return LD_OK;
    }
    return LD_ERR_INTERNAL;
}

